package main

import (
	"fmt"
)

type ProcessData struct {
	processName string  // 程序名
	arriveTime  int     // 到达时间
	startTime   int     // 开始时间
	workTime    int     // 工作时间
	workedTime  int     // 已工作时间
	finishTime  int     // 完成时间
	turnTime    int     // 周转时间
	powerTime   float64 // 带权周转时间
	flag        bool    // 标志第一次运行
}

type Queue struct {
	list     []ProcessData
	turnTime int
}

var (
	queue = [3]Queue{
		{turnTime: 1},
		{turnTime: 2},
		{turnTime: 4},
	}
	cpuTime     = 5 // 定义时间片
	currentTime = 0
	finished    = make([]ProcessData, 0)
)

func main() {
	// 为了方便, 暂定0是优先级最高的队列
	var list []ProcessData
	list = append(list, ProcessData{
		processName: "A",
		arriveTime:  0,
		workTime:    10,
	})
	list = append(list, ProcessData{
		processName: "B",
		arriveTime:  5,
		workTime:    3,
	})
	list = append(list, ProcessData{
		processName: "C",
		arriveTime:  3,
		workTime:    7,
	})
	list = append(list, ProcessData{
		processName: "D",
		arriveTime:  9,
		workTime:    4,
	})
	queue[0].list = list
	fmt.Println("---------算法开始---------")
	MLFQ(queue[:])
	fmt.Println("---------算法结束---------")
	display()
	calAvg()
}

func MLFQ(q []Queue) {
mlfq:
	for {
		// 查找目标队列
		index := findQueue(q)
		// 队列都无进程则结束算法
		if index == -1 {
			break
		}
		// 弹出当前队列第一个进程
		first := &q[index].list[0]
		// 若未达到到达时间, 则加入下一优先级末
		if first.arriveTime > currentTime {
			appendQueue(index)
			continue
		}
		// 如果是第一次运行, 则记录开始时间
		if !first.flag {
			first.startTime = currentTime
			first.flag = !first.flag
		}
		// 按队列时间片运作
		for i := 0; i < q[index].turnTime; i++ {
			currentTime++
			first.workedTime++

			showQueue()

			// 按cpu时间片,将其他优先级队列提升到队首,并开始新一轮循环
			if currentTime%cpuTime == 0 {
				fmt.Println("将其他队列进程提升到队首")
				appendQueue(index)
				wakeQueue()
				continue mlfq
			}

			// 进程结束则进行下一轮计算, 并将完成的进程放入统计列表
			if first.workedTime >= first.workTime {
				first.finishTime = currentTime
				first.turnTime = first.finishTime - first.arriveTime
				first.powerTime = float64(first.turnTime) / float64(first.workTime)
				finished = append(finished, *first)
				q[index].shift()
				continue mlfq
			}
		}

		// 否则将其加入队末
		appendQueue(index)
	}
}

// 按优先度查找有进程的队列
func findQueue(q []Queue) int {
	for i := range q {
		if q[i].hasProcess() {
			return i
		}
	}
	return -1
}

func appendQueue(index int) {
	first := queue[index].shift()
	if index != len(queue)-1 {
		// 加入队末下一级队末
		queue[index+1].list = append(queue[index+1].list, first)
	} else {
		// 加入队末当前队末
		queue[index].list = append(queue[index].list, first)
	}
}

func wakeQueue() {
	for i := range queue {
		if i == 0 {
			continue
		}
		queue[0].list = append(queue[0].list, queue[i].list...)
		queue[i].list = nil
	}
}

func showQueue() {
	fmt.Printf("当前时刻:%d ", currentTime)
	for i := range queue {
		fmt.Printf("队列Q%d进程数:%d ",
			i,
			len(queue[i].list))
	}
	fmt.Println()
}

func display() {
	for _, p := range finished {
		fmt.Printf("进程名: %s 到达时间: %d 开始时间: %d 完成时间: %d 周转时间: %d 带权周转时间: %3.2f 等待时间: %d\n",
			p.processName,
			p.arriveTime,
			p.startTime,
			p.finishTime,
			p.turnTime,
			p.powerTime,
			p.startTime-p.arriveTime,
		)
	}
}

func calAvg() {
	var totalTurn float64
	var totalPower float64
	var totalWait float64
	for i := range finished {
		totalTurn += float64(finished[i].turnTime)
		totalPower += finished[i].powerTime
		totalWait += float64(finished[i].startTime - finished[i].arriveTime)
	}
	length := float64(len(finished))
	fmt.Printf("平均周转时间: %3.2f\n", totalTurn/length)
	fmt.Printf("平均带权周转时间: %3.2f\n", totalPower/length)
	fmt.Printf("平均等待时间: %3.2f\n", totalWait/length)
}

// 判断队列是否包含进程
func (q Queue) hasProcess() bool {
	if len(q.list) != 0 {
		return true
	}
	return false
}

// 弹出队列中第一个进程
func (q *Queue) shift() ProcessData {
	first := q.list[0]
	q.list = q.list[1:]
	return first
}
